﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using Reactor.Environment;
using Reactor.ServiceGrid.Packages;

namespace Reactor.ServiceGrid.Repositories
{
    public class SqlPackageRepository : PackageRepositoryBase
    {
        private readonly string _connectionString;
        private static readonly object RepositoryLock = new Object();
        private LocalPackageRepository _localRepository;

        /// <summary>
        /// Initializes a new instance of the <see cref="SqlPackageRepository"/> class.
        /// </summary>
        /// <param name="platformConnectionData">The platform connection data.</param>
        public SqlPackageRepository(PlatformConnectionData platformConnectionData)
        {
            if (platformConnectionData == null) throw new ArgumentNullException("platformConnectionData");
            if (string.IsNullOrEmpty(platformConnectionData.DatabaseConnectionString)) throw new ArgumentException("No DatabaseConnectionString found on PlatformConnectionData.");

            _connectionString = platformConnectionData.DatabaseConnectionString;

            Log.Debug("Creating local repository for temp location of service packages.");
            CreateLocalRepository();
        }

        #region Overrides of PackageRepositoryBase

        public override string Source
        {
            get { return _localRepository.Source; }
        }

        public override IQueryable<IPackage> GetPackages()
        {
            lock (RepositoryLock)
            {
                if (_localRepository.GetPackages().Count() == 0)
                    ReplaceLocalPackages();
            }

            return _localRepository.GetPackages();
        }

        public override void AddPackage(IPackage package)
        {
            using (var sqlConnection = new SqlConnection(_connectionString))
            {
                using (var cmd = new SqlCommand("dbo.rsp_SaveServicePackage", sqlConnection))
                {
                    cmd.CommandType = CommandType.StoredProcedure;

                    var nameParameter = cmd.CreateParameter();
                    nameParameter.ParameterName = "name";
                    nameParameter.SqlDbType = SqlDbType.NVarChar;
                    nameParameter.Value = package.Id;

                    var versionParameter = cmd.CreateParameter();
                    versionParameter.ParameterName = "version";
                    versionParameter.SqlDbType = SqlDbType.NVarChar;
                    versionParameter.Value = package.Version.ToString();

                    var packageParameter = cmd.CreateParameter();
                    packageParameter.ParameterName = "packageFile";
                    packageParameter.SqlDbType = SqlDbType.Binary;
                    packageParameter.Value = package.GetStream().ReadAllBytes();

                    cmd.Parameters.Add(nameParameter);
                    cmd.Parameters.Add(versionParameter);
                    cmd.Parameters.Add(packageParameter);

                    sqlConnection.Open();
                    cmd.ExecuteNonQuery();
                }
            }
        }

        public override void Refresh()
        {
            lock (RepositoryLock)
            {
                ReplaceLocalPackages();    
            }
        }

        #endregion

        private void ReplaceLocalPackages()
        {
            // Delete local package versions
            var existingPackages = _localRepository.GetPackages();
            foreach (var existingPackage in existingPackages)
                _localRepository.RemovePackage(existingPackage);

            // Obtain new files from the database
            byte[] buffer;
            using (var sqlConnection = new SqlConnection(_connectionString))
            {
                using (var cmd = new SqlCommand("dbo.rsp_GetAllServicePackages"))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    sqlConnection.Open();
                    cmd.Connection = sqlConnection;

                    using (var rdr = cmd.ExecuteReader())
                    {
                        if (rdr != null)
                        {
                            while (rdr.Read())
                            {
                                long size = rdr.GetBytes(3, 0, null, 0, 0); //get the length of data
                                buffer = new byte[size];

                                var packageInfo = new PackageInfo
                                                        {
                                                            ServicePackageId = rdr.GetGuid(0),
                                                            Name = rdr.GetString(1),
                                                            Version = new Version(rdr.GetString(2))
                                                        };
                                rdr.GetBytes(3, 0, buffer, 0, buffer.Length);

                                // Write local file for local repository to pick up
                                var packageFileName = string.Format("{0}.{1}.rspkg", packageInfo.Name, packageInfo.Version);
                                File.WriteAllBytes(Path.Combine(_localRepository.Source, packageFileName), buffer);
                            }
                        }
                    }
                }
            }
        }

        private void CreateLocalRepository()
        {
            // Obtain temp directory
            var reactorTempPath = Path.Combine(Path.GetTempPath(), "Reactor");
            if (!Directory.Exists(reactorTempPath)) Directory.CreateDirectory(reactorTempPath);

            var privateTempPath = Path.Combine(reactorTempPath, Guid.NewGuid().ToString());
            if (!Directory.Exists(privateTempPath)) Directory.CreateDirectory(privateTempPath);

            // Create local repository
            _localRepository =  new LocalPackageRepository(privateTempPath);
        }
    
        private class PackageInfo
        {
            public Guid ServicePackageId { get; set; }
            public string Name { get; set; }
            public Version Version { get; set; }
        }
    }
}
